/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       ai_value.cpp

	$Header: /game/ai_value.h $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "ai_value.h"

#include <memory>

#include "adventure_ai.h"
#include "adventure_map.h"
#include "ai_army_data_cache.h"
#include "army.h"
#include "army_list.h"
#include "artifact_effect_visitor.h"
#include "artifact_prop_ability.h"
#include "artifact_prop_combat.h"
#include "artifact_prop_damage.h"
#include "artifact_prop_growth.h"
#include "artifact_prop_movement.h"
#include "artifact_prop_single_spell.h"
#include "artifact_prop_skill.h"
#include "artifact_prop_spell_attack.h"
#include "artifact_prop_spell_list.h"
#include "artifact_prop_spell_charges.h"
#include "building_traits.h"
#include "creature_array.h"
#include "creature_traits.h"
#include "default_artifacts.h"
#include "default_artifact_properties.h"
#include "enum_operations.h"
#include "get_artifact_damage_modifier.h"
#include "hero.h"
#include "artifact_prop_income.h"
#include "materials.h"
#include "minimum_maximum.h"
#include "player.h"
#include "skill.h"
#include "skill_properties.h"
#include "spell.h"
#include "spell_properties.h"
#include "town.h"
#include "town_list.h"

const float k_ai_creature_value_modifier = 1.0f;
const int k_ai_mine_value_multiplier = 3;  
const float k_ai_experience_value_multiplier = 0.001f;
const float k_ai_army_experience_value_multiplier = 0.0005f;

// 2% for the initiative shuffling and 5% for the additional damage.
// Morale bonus : +25% -20%
const float k_ai_morale_value = 0.07f; 
const float k_ai_luck_value = 0.25f;

// These might be tweaked
const float k_tactics_multiplier[k_mastery_count] =
{
	0.2f,
	0.4f,
	0.6f,
	0.8f,
	1.0f
};

const float k_ai_value_of_extra_strike = 0.30f;

t_spell const k_mock_spell[] =
{
	k_spell_holy_word,
	k_spell_magic_fist,
	k_spell_poison,
	k_spell_firebreathing,
	k_spell_strength
};


// ********************************************
// Generic Support Functions
// ********************************************

bool ai_is_hero_ranged( t_hero const* hero )
{
	return 	hero->ai_get_total_offense( k_offense_ranged ) 
				> hero->ai_get_total_offense( k_offense_melee );
}

bool player_can_afford( t_creature_array const* array, int amount )
{
	t_player* player = array->get_owner();
	assert(player);
	t_material_array const& funds = player->get_funds();
	return (funds[k_gold] >= amount);
}

bool ai_wants_primary_skill( t_hero const* hero, t_skill_type skill, t_creature_array const* array )
{
	// Give the hero a chance to bow out and be selective about primary
	// skill selection.
	return true;
}

int get_player_number( t_creature_array const& array )
{	
	return array.get_map()->get_player_number(array.get_owner()->get_color());
}

float ai_extract_hit_points( float value )
{
	return value * 0.40f;
}

float ai_extract_damage( float value )
{
	return value * 0.60f;
}

float ai_get_spell_power_bonus( t_spell spell, t_hero const* hero)
{
	switch (get_spell_alignment( spell ))
	{
		case k_town_life:
			return ( hero->get_skill( k_skill_healing_magic )
					  + hero->get_skill( k_skill_spirit_magic ) 
					  + get_artifact_spell_power_modifier( *hero, spell )
					  + 2) / 10.0f;
			break;
		case k_town_order:
			return ( hero->get_skill( k_skill_enchantment )
					  + hero->get_skill( k_skill_mind_magic ) 
					  + get_artifact_spell_power_modifier( *hero, spell )
					  + 2) / 10.0f;
			break;
		case k_town_death:
			return ( hero->get_skill( k_skill_black_magic )
					  + hero->get_skill( k_skill_demonology ) 
					  + get_artifact_spell_power_modifier( *hero, spell )
					  + 2) / 10.0f;
			break;
		case k_town_chaos:
			return ( hero->get_skill( k_skill_conjuration )
					  + hero->get_skill( k_skill_destructive_magic ) 
					  + (hero->get_skill( k_skill_intelligence ) + 1) * 2  
					  + get_artifact_spell_power_modifier( *hero, spell )
					  + 2 ) / 10.0f;
			break;
		case k_town_nature:
			return ( hero->get_skill( k_skill_druid_magic )
					  + hero->get_skill( k_skill_meditation ) 
					  + get_artifact_spell_power_modifier( *hero, spell )
					  + 2 ) / 10.0f;
			break;
		default:
			assert(false);
			return 0.0f;
	}
}

float ai_get_value_of_additional_level( t_skill_type type )
{
	if ( type == k_skill_intelligence )
		return 0.2f;
	else
		return 0.1f;
}

float ai_get_spell_damage( t_spell spell, t_hero const* hero )
{
	float multiplier = (1.0f + ai_get_spell_power_bonus( spell, hero )) * 100;

	return (float)get_basic_spell_power( spell, hero->get_level(), multiplier);
}

// ********************************************
// Generic Query Functions
// ********************************************

float ai_bonus_for_speed( int amount ) 
{
	// Will be adjusted
	return (amount / 100.0f);
}

float ai_bonus_for_move_speed( int amount )
{
	// Will be adjusted
	return (amount / 100.0f);
}

float ai_bonus_for_luck( int value )
{
	if (value == 0) 
		return 0.0f;
	
	if (value > 10) 
		value = 10;
	else 
		if (value < -10)
			value = -10;

	return (value / 10.0f) * k_ai_luck_value;
}

float ai_bonus_for_morale( int value )
{
	if (value == 0) 
		return 0.0f;

	if (value > 10) 
		value = 10;
	else 
		if (value < -10)
			value = -10;

	return (value / 10.0f) * k_ai_morale_value;
}

float ai_bonus_for_luck_change( int before, int after )
{
	if (before == after)
		return 0.0f;

	return
		ai_bonus_for_luck( after ) - ai_bonus_for_luck( before );
}

float ai_bonus_for_morale_change( int before, int after )
{
	if (before == after) 
		return 0.0f;
	
	return
		ai_bonus_for_morale( after ) - ai_bonus_for_morale( before );
}

float ai_value_of_luck_change( t_creature_array const& array, t_qualified_adv_object_type const& type, int change )
{
	float value = 0.0f;
	for ( int i = 0; i < t_creature_array::k_size; i++ )
	{
		t_creature_stack const& stack = array[i];
		if ( (stack.get_number() == 0) || stack.has_temporary_bonus( type ) ) 
			continue;

		int luck = stack.get_luck();
		value += stack.ai_value() * ai_bonus_for_luck_change( luck, luck + change );
	}
	return value;
}

float ai_value_of_morale_change( t_creature_array const& array, t_qualified_adv_object_type const& type, int change )
{	
	float value = 0.0f;
	for ( int i = 0; i < t_creature_array::k_size; i++ )
	{
		t_creature_stack const& stack = array[i];
		if ( (stack.get_number() == 0) || stack.has_temporary_bonus( type ) ) 
			continue;

		int morale = stack.get_morale();
		value += stack.ai_value() * ai_bonus_for_morale_change( morale, morale + change );
	}
	return value;
}

float ai_value_of_xp( t_creature_array const* army, int points )
{
	if (army->get_living_hero_count() == 0)
		return 0.0f;

	return points * k_ai_experience_value_multiplier;
}

float ai_value_of_xp_for_army( t_creature_array const* army, int points )
{
	return ai_value_of_xp( army, points );
}

float ai_value_of_additional_move_speed( t_creature_stack const* stack, int amount )
{
	return ai_bonus_for_move_speed(amount) * stack->ai_value();
}

float ai_value_of_additional_speed( t_creature_stack const* stack, int amount )
{
	return ai_bonus_for_speed(amount) * stack->ai_value();
}

float ai_value_of_additional_speed( t_creature_array const* array, int amount )
{
	return ai_bonus_for_speed(amount) * array->ai_value();
}

float ai_value_of_additional_move_speed( t_creature_array const* array, int amount )
{
	return ai_bonus_for_move_speed(amount) * array->ai_value();
}

float ai_value_of_additional_spell_points( t_hero const* hero, int amount )
{
	int spell_points = hero->get_maximum_spell_points();
	if ((spell_points == 0) || hero->is_dead())
		return 0.0f;

	float value = hero->ai_value();

	if (value == 0.0f)
		return 0.0f;

	return value * (((spell_points + amount) / static_cast<float>(spell_points)) - 1.0f);
}

float ai_value_of_additional_hit_points( float current_hit_points, float additional_hit_points, float current_value )
{
	return sqrt( additional_hit_points / current_hit_points ) * current_value;
}

float ai_value_of_additional_hit_points( t_creature_stack const* stack, int amount )
{
	float hit_points = stack->ai_get_total_defense();
	float adjusted_hit_points = amount * stack->ai_get_average_defense();
	return sqrt( adjusted_hit_points / hit_points ) 
			* sqrt( stack->ai_get_total_offense() * hit_points );
}

float ai_value_of_additional_damage( t_hero const* hero, int damage_adjustment, t_offense_type type )
{	
	float damage = hero->ai_get_total_offense( type );
	float new_damage = damage_adjustment * 2;
	if (type != k_offense_spell)
		new_damage *= hero->get_offense( type == k_offense_ranged );
	return sqrt( new_damage / damage ) 
			* sqrt( damage * hero->ai_get_total_defense() );
}

float ai_value_of_additional_damage( t_creature_stack const* stack, bool ranged, int low, int high )
{
	float damage = stack->ai_get_total_offense( ranged );
	float adjusted_damage = (low + high) * stack->get_offense( ranged );
	return sqrt( adjusted_damage / damage ) 
			* sqrt( damage * stack->ai_get_total_defense() );
}

float ai_value_of_additional_damage( t_creature_stack const* stack, bool ranged, int damage_adjustment )
{
	float damage = stack->ai_get_total_offense( ranged );
	float adjusted_damage = damage_adjustment * 2 * stack->get_offense( ranged );
	return sqrt( adjusted_damage / damage )
			* sqrt( damage * stack->ai_get_total_defense() );
}

float ai_value_of_additional_damage( t_creature_stack const* stack, int damage_adjustment )
{
	float damage = maximum( stack->ai_get_total_offense( true ), 
		stack->ai_get_total_offense( false ) );
	return sqrt( damage_adjustment * 2 / damage ) 
			* sqrt( damage * stack->ai_get_total_defense() );
}

float ai_value_of_additional_damage( t_creature_stack const* stack, int low, int high )
{
	int melee_offense = stack->ai_get_total_offense( false );
	int ranged_offense = stack->ai_get_total_offense( true );

	if (ranged_offense > melee_offense)
	{
		return sqrt( (float)(((low + high) * stack->get_offense(true)) / ranged_offense) ) 
			* sqrt( (float)(ranged_offense * stack->ai_get_total_defense()) );
	}
	else
	{
		return sqrt( (float)(((low + high) * stack->get_offense(false)) / melee_offense) ) 
			* sqrt( (float)(melee_offense * stack->ai_get_total_defense()) );
	}
}

// ********************************************
// Skill Query Functions
// ********************************************

float const k_ai_generic_multiplicative_spell_value = 0.05f;

const float k_necro_factor[] =
{
	0.10000f,
	0.04545f,
	0.04347f,
	0.04166f,
	0.04000f
};

const float k_charm_factor[] = 
{
	0.15000f,
	0.04347f,
	0.04166f,
	0.04000f,
	0.03846f
};

const float k_res_factor[] =
{
	0.20000f,
	0.08333f,
	0.07692f,
	0.03571f,
	0.03448f
};

inline float ai_bonus_for_leadership_skill( t_skill_mastery mastery, t_skill_mastery tactics_mastery )
{
	return ai_bonus_for_morale( mastery + 1 ) * k_tactics_multiplier[tactics_mastery + 1];
}

inline float ai_bonus_for_tactics_skill( t_skill_mastery mastery )
{
	return ai_bonus_for_speed( mastery + 1 ) * k_tactics_multiplier[mastery + 1];
}

float ai_bonus_for_generalship_skills( t_creature_array const* array, bool include_dead_heroes )
{
	assert( array );
	
	t_skill_mastery
		best_offense = k_mastery_none,
		best_offense_tactics = k_mastery_none,
		best_defense = k_mastery_none,
		best_defense_tactics = k_mastery_none,
		best_leadership = k_mastery_none,
		best_leadership_tactics = k_mastery_none,
		best_tactics = k_mastery_none,
		value = k_mastery_none;

	for (int i = 0; i < t_creature_array::k_size; i++) 
	{
		t_hero const* hero = (*array)[i].get_const_hero();
		if (!hero || (hero->is_dead() && !include_dead_heroes)) 
			continue;
		
		value = hero->get_skill(k_skill_offense);

		t_skill_mastery tactics = hero->get_skill(k_skill_tactics);
		if (value > best_offense) 
		{
			best_offense = value;
			best_offense_tactics = tactics;
		}

		value = hero->get_skill(k_skill_defense);
		if (value > best_defense) {
			best_defense = value;
			best_defense_tactics = tactics;
		}

		value = hero->get_skill(k_skill_leadership);
		if (value > best_defense) {
			best_leadership = value;
			best_leadership_tactics = tactics;
		}

		if (tactics > best_tactics) 
			best_tactics = tactics;

	}

	float offense = ((best_offense + 1) / 20) * k_tactics_multiplier[best_offense];
	float defense = ((best_defense + 1) / 20) * k_tactics_multiplier[best_defense];
	
	// This calculates the percentage added to the creatures
	// based on the additional damage imparted by the generalship
	// skills.   
	float overall = 
			offense 
			+ defense 
			+ ai_bonus_for_leadership_skill( best_leadership, best_leadership_tactics )
			+ ai_bonus_for_tactics_skill( best_tactics ) ;

	return overall;
}

float ai_value_of_related_skills( t_skill skill, t_hero const* hero, t_creature_array const* array )
{
	if ( is_primary_skill(skill.skill) ) 
	{
		// Possibly assess the value of secondary skills and this skills contribution
		// towards attaining those skills.
		return 0.0f;
	}
	else 
	{
		float value = 0.0f;
		std::vector<t_skill> const& reqs = get_requirements( skill );
		int requirement_count = reqs.size();
		
		for (int i = 0; i < requirement_count; i++) 
		{
			t_skill const& required_skill = reqs[i];
			if ( (skill.level == required_skill.level) && (skill.skill == required_skill.skill) ) 
				value += ai_value_of_skill_increase( hero, skill, array, false );
		}
		return value / requirement_count;
	}
}

float ai_value_of_tactics_increase( t_hero const* hero, t_skill_mastery mastery, t_creature_array const* array )
{
	// 1 point of move speed per increase, scaled by the change in the tactics multiplier.
	float tactics_multiplier = ( k_tactics_multiplier[mastery] - k_tactics_multiplier[enum_subtract( mastery, 1)] );
	float value = ai_value_of_additional_move_speed( array, 1 ) * tactics_multiplier;

	// Add the additional value added to the tactical skills
	float bonus =
		((hero->get_skill(k_skill_offense) + (hero->get_skill(k_skill_defense) + 2) / 20) +
		ai_bonus_for_morale( hero->get_skill( k_skill_leadership ) + 1)) *
		tactics_multiplier;

	value += array->ai_value_creatures() * bonus;

	return value;
}

float ai_value_of_combat_increase( t_hero const* hero, t_skill_mastery mastery )
{
	//	Adv Defense		Level		1		2		3		4		5
	//
	//		1.0						0.0		0.0		0.0		0.0		0.0
	//		1.5						0.5		0.5		0.5		0.5		0.5
	//		2.2						0.73	1.1		2.2		2.2		2.2
	//		3.4						1.13	1.7		2.26	3.4		3.4
	//
	//	Adv decrease-> 				0.59	0.82	1.24	1.525	1.525
	//	Skill's Offense bonus->		0.5		1.0		2.0		3.0		3.0
	//	Total Bonus->				1.09	1.82	3.24	4.525	4.525  
	//	Value Increase (TB+1.0/2)->	1.045	1.41	2.12	2.7625	2.7625
	
	static const float k_combat_damage_modifier[k_mastery_count] =
	{
		1.045f,
		0.349f,
		0.503f,
		0.303f,
		0.30f
	};
	return k_combat_damage_modifier[mastery] * hero->ai_value();
}

float ai_value_of_archery_increase( t_hero const* hero, t_skill_mastery mastery )
{
	static const float k_archery_damage_modifier[k_mastery_count] =
	{
		0.25f,
		0.165f,
		0.25f,  // first strike not included
		0.165f,
		k_ai_value_of_extra_strike	// Only the advantage of double attack
	};
	return k_archery_damage_modifier[mastery] * hero->ai_value();
}

float ai_value_of_scouting_increase( t_hero const* hero, t_skill_mastery mastery, t_creature_array const* array )
{
	// This is pretty arbitrary at this point...  Scaling the value of the entire army
	// based on the value of the scouting skill is probably questionable.
	static const float k_scouting_value[k_mastery_count] =
	{
		0.030f,	// (1.03 / 1.00) - 1.0
		0.048f,	// (1.08 / 1.03) - 1.0
		0.065f,	// (1.15 / 1.08) - 1.0
		0.060f,	// (1.22 / 1.15) - 1.0
		0.065f	// (1.30 / 1.22) - 1.0
	};
	return k_scouting_value[mastery] * array->ai_value();
}

float ai_value_of_toughness_increase( t_hero const* hero, t_skill_mastery mastery )
{
	static const float k_toughness_value[k_mastery_count] =
	{
		0.25f,
		0.165f,
		0.25f,
		0.165f,
		0.25f
	};
	return k_toughness_value[mastery] * hero->ai_value();
}

float ai_value_of_resistance_increase( t_hero const* hero, t_skill_mastery mastery )
{
	// Overall resistance is worth 30%.  These values are base off of that.
	static const float k_resistance_value[k_mastery_count] =
	{
		0.10f,  // (1.10 / 1.00) - 1.0
		0.045f,	// (1.15 / 1.10) - 1.0
		0.052f, // (1.21 / 1.15) - 1.0
		0.024f, // (1.24 / 1.21) - 1.0
		0.048f  // (1.30 / 1.24) - 1.0
	};
	return k_resistance_value[mastery] * hero->ai_value();
}

float ai_value_of_nobility_increase( t_hero const* hero, t_skill_mastery mastery, t_creature_array const* array )
{
	t_abstract_town* town = hero->get_nobility_town();
	if (!town)
		return 0.0f;

	float unadjusted_value = 0.0f;
	t_town_building building = k_town_dwelling_1;
	while (building <= k_town_dwelling_8) 
	{
		if (town->has(building)) 
		{
			t_creature_type creature = get_traits( town->get_type(), building ).creature;
			int	growth = get_traits( creature ).weekly_growth;
			unadjusted_value += get_traits( creature ).experience;
		}
		enum_incr(building, 1);
	}	

	float nobility_difference = hero->get_skill_power( t_skill( k_skill_nobility, mastery ) ) - town->get_nobility_bonus();
	
	if (nobility_difference <= 0) 
		return 0.0f;
	else
		return unadjusted_value * (nobility_difference / 100.0f);
}

float ai_value_of_leadership_increase( t_hero const* hero, t_skill_mastery mastery, t_creature_array const* array )
{
	return
		ai_bonus_for_morale( hero->get_skill( k_skill_leadership ) + 1 ) * k_tactics_multiplier[mastery] * array->ai_value_creatures();
}

float ai_value_of_pathfinding_increase( t_hero const* hero, t_skill_mastery mastery, t_creature_array const* array )
{
	static const float k_pathfinding_value[k_mastery_count] =
	{
		0.125f,
		0.150f,
		0.200f,
		0.200f,
		0.200f
	};

	t_adventure_ai const& ai = array->get_map()->get_ai();
	return k_pathfinding_value[mastery] * 
			ai.get_rough_coverage() * 
			array->ai_value();
}

float ai_value_of_seamanship_increase( t_hero const* hero, t_skill_mastery mastery, t_creature_array const* array )
{
	static const float k_seamanship_value[k_mastery_count] =
	{
		0.125f,
		0.150f,
		0.200f,
		0.200f,
		0.200f
	};

	return k_seamanship_value[mastery] * 
			(1.0f - array->get_map()->get_ai().get_land_coverage()) * 
			array->ai_value();
}

float ai_value_of_stealth_increase( t_hero const* hero, t_skill_mastery mastery, t_creature_array const* array )
{
	static const int k_stealth_creature_value[k_mastery_count] =
	{
		155,
		155,
		129,
		181,
		621
	};
	return k_stealth_creature_value[mastery] +
			(k_stealth_creature_value[mastery] * 
			array->get_map()->get_day() / 7);
}

float ai_value_of_estates_increase( t_hero const* hero, t_skill_mastery mastery, t_creature_array const* array )
{
	// The increases in weeks worth of growth
	static const int k_estates_gold_value[k_mastery_count] =
	{
		700,	// 700,  // 100,
		1050,	// 1750, // 250,
		1400,	// 3150, // 450,
		1750,	// 4900, // 700,
		2100,	// 7000  // 1000
	};
	return k_estates_gold_value[mastery] * array->get_owner()->get_material_value(k_gold);
}

float ai_value_of_mining( t_hero const* hero, t_skill_mastery mastery, t_creature_array const* array )
{
	t_material_values const& values = array->get_owner()->get_material_values();
	float value = 0.0f;
	switch (mastery)
	{
		case k_mastery_basic:	
			// An ore and a wood every 5 days
			value = (values[k_wood] + values[k_ore]) * ( 7.0f / 5.0f );
			break;
		case k_mastery_advanced:
			// An ore and a wood every 5 days
			// A percious gem every 10 days
			value = (values[k_wood] * values[k_ore]) * ( 7.0f / 5.0f );
			value += (values[k_crystal] + values[k_crystal] +
					  values[k_mercury] + values[k_sulfur]) * ( 7.0f / 10.0f );
			break;
		case k_mastery_expert:
			// An ore and a wood every 3 days
			// A percious gem every 7 days
			value = (values[k_wood] * values[k_ore]) * ( 7.0f / 3.0f );
			value += (values[k_crystal] + values[k_crystal] +
					  values[k_mercury] + values[k_sulfur]);
			break;
		case k_mastery_master:
			// An ore and a wood every 2 days
			// A percious gem every 4 days
			value = (values[k_wood] * values[k_ore]) * ( 7.0f / 2.0f );
			value += (values[k_crystal] + values[k_crystal] +
					  values[k_mercury] + values[k_sulfur]) * ( 7.0f / 4.0f );
			break;
		case k_mastery_grand_master:
			// An ore and a wood every 1 days
			// A percious gem every 2 days
			value = (values[k_wood] * values[k_ore]) * 7.0f;
			value += (values[k_crystal] + values[k_crystal] +
					  values[k_mercury] + values[k_sulfur]) * (7.0f / 2.0f);
			break;
	}
	return value;
}

float ai_value_of_mining_increase( t_hero const* hero, t_skill_mastery mastery, t_creature_array const* array )
{
	return ai_value_of_mining( hero, mastery, array ) - ai_value_of_mining( hero, enum_subtract( mastery, 1 ), array );	
}

float ai_value_of_generic_damage_spell_amount( t_hero const* hero, int damage );

float ai_value_of_magic_skill_increase( t_town_type alignment, t_hero const* hero, t_skill skill, t_creature_array const* array )
{
	if (!hero->can_learn(skill))
		return 0.0f;

	t_adventure_ai const& ai = array->get_map()->get_ai();

	int alignment_level = hero->get_skill(static_cast<t_skill_type>(alignment + k_skill_life_magic)) + 1;

	float spell_power = ai_get_spell_power_bonus( k_mock_spell[alignment], hero );

	float raw_damage = (30 + alignment_level * 10 + 
						(3 + alignment_level) * hero->get_level() );

	float total_defense = hero->ai_get_total_defense();
	
	if (is_primary_skill( skill.skill ))
	{
//		if (ai.get_buildable_mage_guild_level() < skill.level) 
//			return 0.0f;

		float linear_before =
				sqrt( raw_damage * spell_power * 2 * total_defense );

		spell_power += ai_get_value_of_additional_level( skill.skill );
		raw_damage += 10 + hero->get_level();

		float linear = ai_value_of_generic_damage_spell_amount( hero, raw_damage * spell_power );

		float multiplicative_difference = array->ai_value() 
			* k_ai_generic_multiplicative_spell_value;

		return maximum(multiplicative_difference, linear);
	}

	float linear = ai_value_of_generic_damage_spell_amount( hero, raw_damage * spell_power );

	return linear > 0.0f ? linear : 0.0f;
}

float ai_value_of_skill_increase(t_hero const* hero, t_skill skill, t_creature_array const* array, bool include_related_skills) 
{
	assert(hero);

	if ( !hero->can_learn(skill) || (skill.level <= hero->get_skill(skill.skill)) )
		return 0.0f;

	float value = 0.0f;
	switch (skill.skill) 
	{
		case k_skill_tactics:
			value = ai_value_of_tactics_increase( hero, skill.level, array ); 
			break;
		case k_skill_offense:
		case k_skill_defense:
			{
				float tactics_bonus = k_tactics_multiplier[skill.skill];
				if (skill.level > k_mastery_basic)
					tactics_bonus -= k_tactics_multiplier[enum_subtract(skill.level, 1)];
	
				value = 0.05f * tactics_bonus * array->ai_value_creatures(false);
			}
			break;
		case k_skill_leadership:
			value = ai_value_of_leadership_increase( hero, skill.level, array );
			break;

		case k_skill_melee:
			value = ai_value_of_combat_increase( hero, skill.level );
			break;

		case k_skill_combat_defense:
			value = ai_value_of_toughness_increase( hero, skill.level );
			break;
		case k_skill_archery:
			value = ai_value_of_archery_increase( hero, skill.level );
			break;
		case k_skill_magic_resistance:
			value = ai_value_of_resistance_increase( hero, skill.level );
			break;
			
		case k_skill_scouting:
			value = ai_value_of_scouting_increase( hero, skill.level, array );
			break;
		case k_skill_pathfinding:
			value = ai_value_of_pathfinding_increase( hero, skill.level, array );
			break;
		case k_skill_seamanship:
			value = ai_value_of_seamanship_increase( hero, skill.level, array );
			break;
		case k_skill_stealth:
			value = ai_value_of_stealth_increase( hero, skill.level, array );
			break;

		case k_skill_nobility:
			value = ai_value_of_nobility_increase( hero, skill.level, array );
			break;
		case k_skill_estates:
			value = ai_value_of_estates_increase( hero, skill.level, array );
			break;
		case k_skill_mining:
			value = ai_value_of_mining_increase( hero, skill.level, array );
			break;
		case k_skill_diplomacy:
			value = 0.0f;
			// Still being worked out...
			break;

		case k_skill_life_magic:
		case k_skill_healing_magic:
		case k_skill_spirit_magic:
			value = ai_value_of_magic_skill_increase( k_town_life, hero, skill, array );
			break;

		case k_skill_resurrection:
			{
				value = array->ai_value() * k_res_factor[skill.level];
			}
			break;

		case k_skill_order_magic:
		case k_skill_enchantment:
		case k_skill_mind_magic:
			value = ai_value_of_magic_skill_increase( k_town_order, hero, skill, array );
			break;

		case k_skill_charm:
			{
				static const float charm_cap[] =
				{
					60.0f,
					120.0f,
					180.0f,
					240.0f,
					300.0f
				};

				value = array->ai_value() * k_charm_factor[skill.level];
				float cap = charm_cap[skill.level] * (1.0f + (0.1 * hero->get_level()));
				if (value > cap) value = cap;
			}
			break;

		case k_skill_death_magic:
		case k_skill_black_magic:
		case k_skill_demonology:
			value = ai_value_of_magic_skill_increase( k_town_death, hero, skill, array );
			break;

		case k_skill_necromancy:
			{
				static const float necro_cap[] =
				{
					40.0f,
					40.0f,
					120.0f,
					160.0f,
					200.0f
				};

				value = array->ai_value() * k_necro_factor[skill.level];
				float cap = necro_cap[skill.level] * (1.0f + (0.1 * hero->get_level()));
				if (value > cap) value = cap;
			}
			break;


		case k_skill_chaos_magic:
		case k_skill_conjuration:
		case k_skill_destructive_magic:
		case k_skill_intelligence:
			value = ai_value_of_magic_skill_increase( k_town_chaos, hero, skill, array );
			break;

		case k_skill_nature_magic:
		case k_skill_druid_magic:
		case k_skill_meditation:
			value = ai_value_of_magic_skill_increase( k_town_nature, hero, skill, array );
			break;

		case k_skill_recruitment:
			{
				float summoned = (skill.level * 20);
				value = summoned + (summoned * hero->get_level() * 0.1);
			}
			break;
	}

	if (include_related_skills)
		value += ai_value_of_related_skills( skill, hero, array );
		
	return value;
}

float ai_value_of_skill_increase_minus_cost(t_hero const* hero, t_skill skill, int cost, t_creature_array const* array, bool include_related_skills)
{
	float value = ai_value_of_skill_increase( hero, skill, array, include_related_skills );
	value -= cost * array->get_owner()->get_material_value( k_gold );
	return value;
}

float ai_average_skill_value(float cost, t_skill_type type_low, t_skill_type type_high, t_creature_array const* array, t_single_use_object const* object )
{
	float value = 0.0f;
	for (int i = 0; i < t_creature_array::k_size; i++) 
	{
		t_hero const* hero  = (*array)[i].get_const_hero();
		if (!hero || (object && hero->has_visited(*object))) continue;

		float	total_value = 0.0f;
		int		num_counted = 0;
		for (t_skill_type type = type_low; type <= type_high; enum_incr(type, 1))
		{
			if ( (hero->get_skill(type) == k_mastery_none) &&
				hero->can_learn(t_skill(type, k_mastery_basic)) )
			{
				float skill_value = ai_value_of_skill_increase(hero, t_skill(type, k_mastery_basic), array) - cost;
				if (skill_value > 0.0f) 
				{
					total_value += skill_value;
					num_counted++;
				}
			}
		}
		value += total_value / num_counted;
	}
	return value;
}

float ai_average_of_upgradable_skill_value(float cost, t_skill_type type_low, t_skill_type type_high, t_creature_array const* array, t_single_use_object const* object )
{
	float value = 0.0f;
	for (int i = 0; i < t_creature_array::k_size; i++) 
	{
		t_hero const* hero  = (*array)[i].get_const_hero();
		if (!hero || (object && hero->has_visited(*object))) continue;

		float	total_value = 0.0f;
		int		num_counted = 0;
		for (t_skill_type type = type_low; type <= type_high; enum_incr(type, 1))
		{
			t_skill_mastery mastery = hero->get_skill(type);
			if ( (mastery > k_mastery_none) && (mastery < k_mastery_grand_master)
				&& 	hero->can_learn(t_skill(type, enum_add(mastery, 1))) )
			{
				float skill_value = ai_value_of_skill_increase(hero, t_skill(type, k_mastery_basic), array) - cost;
				if (skill_value > 0.0f) 
				{
					total_value += skill_value;
					num_counted++;
				}
			}
		}
		value += total_value / num_counted;
	}
	return value;
}

float ai_average_of_upgradable_skill_value( t_hero const* hero, float cost, t_skill_type type_low, t_skill_type type_high, t_creature_array const* array, t_single_use_object const* object )
{
	assert(hero);

	float	total_value = 0.0f;
	int		num_counted = 0;
	for (t_skill_type type = type_low; type <= type_high; enum_incr(type, 1))
	{
		t_skill_mastery mastery = hero->get_skill(type);
		if ( (mastery > k_mastery_none) && (mastery < k_mastery_grand_master)
			&& 	hero->can_learn(t_skill(type, enum_add(mastery, 1))) )
		{
			float skill_value = ai_value_of_skill_increase(hero, t_skill(type, k_mastery_basic), array) - cost;
			if (skill_value > 0.0f) 
			{
				total_value += skill_value;
				num_counted++;
			}
		}
	}
	return total_value / num_counted;
}

float ai_summed_skill_value(float cost, t_skill_type type_low, t_skill_type type_high, t_creature_array const* array )
{
	float value = 0.0f;
	for (int i = 0; i < t_creature_array::k_size; i++) 
	{
		t_hero const* hero  = (*array)[i].get_const_hero();
		if (!hero) continue;

		for (t_skill_type type = type_low; type <= type_high; enum_incr(type, 1))
		{
			if ( (hero->get_skill(type) == k_mastery_none) &&
				hero->can_learn(t_skill(type, k_mastery_basic)) )
			{
				float skill_value = ai_value_of_skill_increase(hero, t_skill(type, k_mastery_basic), array) - cost;
				if (skill_value > 0.0f) 
					value += skill_value;
			}
		}
	}
	return value;
}

float ai_summed_primary_skill_value( float cost, bool secular, t_creature_array const* array, t_single_use_object const* object )
{
	t_skill_type low_skill, high_skill;
	if (secular) 
	{
		low_skill = k_skill_tactics;
		high_skill = k_skill_nobility;
	}
	else 
	{
		low_skill = k_skill_life_magic;
		high_skill = k_skill_nature_magic;
	}

	float value = 0.0f;
	for (int i = 0; i < t_creature_array::k_size; i++) 
	{
		t_hero const* hero  = (*array)[i].get_const_hero();
		if (!hero || (hero->get_primary_skill_count() >= k_primary_skill_limit) ||
				(object && hero->has_visited(*object)) )
			continue;

		int		interest_count = 0;
		float	interest_value = 0.0f;
		for (t_skill_type type = low_skill; type <= high_skill; enum_incr(type, 1))
		{
			if ( ( hero->get_skill( type ) > k_mastery_none ) || !ai_wants_primary_skill( hero, type, array ) )
				continue;
		
			float actual_value = ai_value_of_skill_increase_minus_cost(hero, t_skill(type, k_mastery_basic), cost, array); 
			if (actual_value > 0.0f)
			{
				interest_count++;
				interest_value += actual_value;
			}
		}
		value += interest_value / interest_count;
	}
	return value;
}

// ********************************************
// Spell Query Functions
// ********************************************

float ai_get_slayer_value_multiplier( int day )
{
	float army_multiplier = 0.1f;
	int week = day / 7;
	if ( week > 2 ) 
		army_multiplier = (week - 1) * 0.1;
	if (army_multiplier > 0.6f) 
		army_multiplier = 0.6f;
	return army_multiplier;
}

float ai_value_of_evil_hour( t_hero const* hero, t_creature_array const* array, float army_value )
{
	float our_value = 0.0f;
	float generalship_modifier = ai_bonus_for_generalship_skills(array);

	// Figure out the negative impact to our army.
	for (int i = 0; i < t_creature_array::k_size; i++)
	{
		t_creature_stack const& stack = (*array)[i];
		if (stack.get_alignment() == k_town_life) {
			float stack_value = stack.ai_value(); 
			if (stack.get_const_creature())
				stack_value *= 1.0f + generalship_modifier;
			our_value += stack_value * 0.20f;
		}
	}
	
	// (20% of army value = 1/5 alignments) * 20% value decrease 
	return (army_value * 0.40) - our_value;
}

float ai_value_of_prayer_spell( t_hero const* hero, t_creature_array const* array )
{
	float our_value = 0.0f;
	float generalship_modifier = ai_bonus_for_generalship_skills(array);

	float spell_potency = 0.25f;

	for (int i = 0; i < t_creature_array::k_size; i++)
	{
		t_creature_stack const& stack = (*array)[i];
		t_town_type type = stack.get_alignment();
		float stack_value = stack.ai_value(); 
		if (stack.get_const_creature())
			stack_value *= 1.0f + generalship_modifier;
		if (stack.get_alignment() == k_town_life)
			our_value += stack_value * spell_potency;
	}
	
	return our_value;
}

float ai_value_of_drain_life_spell( t_hero const* hero, t_creature_array const* array, float army_value )
{
	float our_value = 0.0f;
	float generalship_modifier = ai_bonus_for_generalship_skills(array);

	float points_drained = ai_get_spell_damage( k_spell_life_drain, hero );

	// Figure out how much damage we're taking.  
	// This is counted exclusively as a negative effect...  I don't
	// calculate the value added to the hero from friendly drains.
	for (int i = 0; i < t_creature_array::k_size; i++)
	{
		t_creature_stack const& stack = (*array)[i];
		t_town_type type = stack.get_alignment();
		float stack_value = stack.ai_value(); 
		if (stack.get_const_creature())
			stack_value *= 1.0f + generalship_modifier;
		if ((type == k_town_life) || (type == k_town_nature)) 
		{
			float offense = hero->ai_get_total_offense( k_offense_melee );
			if (offense < points_drained)
				our_value = stack_value;
			else
				our_value = stack_value * ( points_drained / offense );
		}
	}

	// We'll assume we're draining 2 stacks.
	float drain_value = ai_value_of_additional_hit_points( hero, points_drained * 2) ;

	// We'll assume we're draining from 20% of the entire army;
	float affected_portion = army_value * 0.2f;
	int hit_points = ai_extract_hit_points( affected_portion );
	if (points_drained > hit_points) 
		return drain_value - our_value;
	else
		return drain_value + (points_drained / hit_points) * affected_portion - our_value;
}

float ai_value_of_celestial_armor_spell( t_hero const* hero, t_creature_array const* array, float army_value )
{
	float points = ai_get_spell_damage( k_spell_celestial_armor, hero ) ;
	return ai_value_of_additional_hit_points( hero, points );
}

float ai_value_of_vampiric_touch( t_hero const* hero, t_creature_array const* array )
{
	float army_value, best_value;
	t_creature_stack const* best_stack = array->ai_get_most_powerful(best_value, army_value);
	
	float offense_boost = best_stack->ai_get_total_offense( false ) * 0.5f;

	return ai_value_of_additional_damage( best_stack, offense_boost );
}

// Generic spell value queries

float ai_value_of_generic_spell( t_hero const* hero, t_creature_array const* array, t_town_type alignment, int level )
{
	t_skill_mastery mastery = hero->get_skill(static_cast<t_skill_type>(k_skill_life_magic + alignment));
	if (mastery < level - 1)
		return 0.0f;

	float spell_power = ai_get_spell_power_bonus( k_mock_spell[alignment], hero );

	float raw_damage = (30 + mastery * 10 + 
						(3 + mastery) * hero->get_level() );

	float linear = ai_value_of_generic_damage_spell_amount( hero, raw_damage * spell_power );

	float multiplicative = array->ai_value() * k_ai_generic_multiplicative_spell_value;

	return maximum(multiplicative, linear);
}

float ai_value_of_generic_damage_spell_amount( t_hero const* hero, int damage )
{
	float new_value = sqrt( (float)(damage * 2 * hero->ai_get_total_defense()) );

	return new_value - hero->ai_value();
}

float ai_value_of_generic_damage_spell( t_hero const* hero, t_spell spell, float multiplier)
{
	int damage = ai_get_spell_damage( spell, hero ) * multiplier;

	return ai_value_of_generic_damage_spell_amount( hero, damage );
}

float ai_value_of_augmented_attack_spell( t_hero const* hero, t_creature_array const* array)
{
	float best_addition = 0.0f;
	int damage = ai_get_spell_damage( k_spell_firebreathing, hero );	
	for (int i = 0; i < t_creature_array::k_size; i++)
	{
		t_creature_stack const& stack = (*array)[i];

		float value = ai_value_of_additional_damage( &stack, damage );
		if (value > best_addition)
			best_addition = value;
	}
	return best_addition;
}

float ai_value_of_generic_healing_spell( t_hero const* hero, t_spell spell, t_creature_array const* array )
{
	int best_hits = 0;
	t_creature_stack const* best_stack = NULL;

	for (int i = 0; i < t_creature_array::k_size; i++)
	{
		t_creature_stack const& stack = (*array)[i];
		int stack_hits = stack.get_hit_points();
		if (stack_hits > best_hits) 
		{
			best_stack = &stack;
			best_hits = stack_hits;
		}
	}

	int healing = ai_get_spell_damage( spell, hero );

	int hit_points = best_stack->get_hit_points();
	if (healing > best_hits)
		healing = best_hits;

	float new_value = sqrt( (float)((best_stack->ai_get_total_offense(false) + healing) * best_stack->ai_get_total_defense( false )) );

	return new_value - best_stack->ai_value();
}

float ai_value_of_generic_mass_healing_spell( t_hero const* hero, t_spell spell, t_creature_array const* array )
{
	float total_value = 0.0f;
	int healing = ai_get_spell_damage( spell, hero );
	int total_healing;

	for (int i = 0; i < t_creature_array::k_size; i++)
	{
		t_creature_stack const& stack = (*array)[i];
		int stack_hits = stack.get_hit_points();
		if (healing > stack_hits)
			total_healing = stack_hits;
		else
			total_healing = healing;
		
		float new_value = sqrt( (float)((stack.ai_get_total_offense(false) + healing) * stack.ai_get_total_defense( false )) );

		total_value += new_value - stack.ai_value();
	}
	return total_value;
}

float ai_value_of_summoning_spell( t_hero const* hero, t_spell spell )
{
	return ai_get_spell_damage( spell, hero );
}

float ai_value_of_multiplicative_spell( float value, t_spell spell )
{
	return value * get_spell_level( spell ) * 0.1;
}

float ai_value_of_healing_spell( t_hero const* hero, t_spell spell, t_creature_array const* array )
{
	switch (spell) 
	{
		case k_spell_regeneration:
			return ai_value_of_generic_healing_spell( hero, spell, array ) * 3;
		case k_spell_divine_intervention:
		case k_spell_mass_healing:
			return ai_value_of_generic_mass_healing_spell( hero, spell, array );
		default:
			return ai_value_of_generic_healing_spell( hero, spell, array );
	}
}

float ai_value_of_generic_spell( t_hero const* hero, t_spell spell, t_creature_array const* array )
{
	// Generally the most powerful stack is 50% of the overall army value.
	// Our evaluation is assuming we're fighting an equal army.
	float army_value = array->ai_value();

	switch (spell) 
	{
		case k_spell_fire_aura_effect:
		case k_spell_fire_shield:
		case k_spell_panic:
			return ai_value_of_multiplicative_spell( army_value / 2, spell );

		case k_spell_rock_growth:  // The AI won't be using these...  hence the tiny value.
		case k_spell_sanctuary:
			return army_value * 0.005;

		case k_spell_summon_boat:
			return army_value * array->get_map()->get_ai().get_land_coverage();


		// The following are intended to fall thru to the default.
		/*
		case k_spell_hand_of_death:
		case k_spell_town_gate:
		case k_spell_mana_leech:
		case k_spell_mire:
		case k_spell_pathfinding:
		case k_spell_sacrifice:
		*/
		default:
			return ai_value_of_multiplicative_spell( army_value, spell );
	}
}
float ai_value_of_curse_spell( t_hero const* hero, t_spell spell, t_creature_array const* array )
{
	// Generally the most powerful stack is 50% of the overall army value.
	// Our evaluation is assuming we're fighting an equal army.
	float army_value = array->ai_value();
	float stack_value = army_value / 2;  

	switch (spell) 
	{
		// ****** Stack related *******
		case k_spell_berserk: 
		case k_spell_blind:
		case k_spell_cancellation:
		case k_spell_cloud_of_confusion:
		case k_spell_confusion:
		case k_spell_cowardice:
		case k_spell_forgetfulness:
		case k_spell_order_slow:
		case k_spell_slow:
		case k_spell_song_of_peace:
		case k_spell_steal_enchantment:
		case k_spell_summon_stinkbug:
		case k_spell_terror:
			return ai_value_of_multiplicative_spell( stack_value, spell );
	
		case k_spell_acid:
			return stack_value * 0.10f;
		case k_spell_curse:
			return stack_value * 0.09f;
		case k_spell_despair: 
			return stack_value * 1.15f * ai_bonus_for_morale(2);
		case k_spell_evil_hour:
			return ai_value_of_evil_hour( hero, array, army_value );
		case k_spell_gas:
			return stack_value * 0.15f;
		case k_spell_hypnotize:
			return stack_value * 0.60f; 
		case k_spell_mass_curse:
			return army_value * 0.09f;
		case k_spell_mass_misfortune:
			return army_value * k_ai_luck_value;
		case k_spell_mass_sorrow:
			return army_value * k_ai_morale_value;
		case k_spell_mass_weakness:
			return army_value * 0.11f;
		case k_spell_misfortune:
			return stack_value * k_ai_luck_value;
		case k_spell_ray_of_suffering:
			return stack_value * 0.2f;
		case k_spell_sorrow:
			return k_ai_morale_value * stack_value;
		case k_spell_weakness:
			return stack_value * 0.11f;

		// The following are intended to fall thru to the default.
		/*		
		case k_spell_banish:
		case k_spell_spell_shackle:
		case k_spell_magic_leech:
		case k_spell_mass_cancellation:
		case k_spell_mass_dispel:
		case k_spell_mass_slow:
		case k_spell_power_drain:
		case k_spell_steal_all_enchantments:
		*/
		default:
			return ai_value_of_multiplicative_spell( army_value, spell );
	}
}

float ai_value_of_damage_spell( t_hero const* hero, t_spell spell, t_creature_array const* array )
{
	switch (spell) 
	{
		case k_spell_firebreathing:
		case k_spell_poison_attack:
			return ai_value_of_augmented_attack_spell( hero, array );

		case k_spell_fire_ring:
		case k_spell_kreegan_fire:
			return ai_value_of_generic_damage_spell( hero, spell, 1.05f );

		case k_spell_holy_retribution:
		case k_spell_poison:
			return ai_value_of_generic_damage_spell( hero, spell, 3.0f );

		case k_spell_burning_hands:
			return ai_value_of_generic_damage_spell( hero, spell, 1.10f );
		case k_spell_chain_lightning:
			return ai_value_of_generic_damage_spell( hero, spell, 1.94f );
		case k_spell_fireball:
			return ai_value_of_generic_damage_spell( hero, spell, 1.15f );
		case k_spell_inferno:
			return ai_value_of_generic_damage_spell( hero, spell, 1.25f );
		case k_spell_life_drain:
			return ai_value_of_drain_life_spell( hero, array, array->ai_value() );

		// The following are intended to fall thru to the default.
		/*
		case k_spell_armageddon:	// This spell should be dealt with differently (effects everyone.)
		case k_spell_disintegrate:
		case k_spell_fire_bolt:
		case k_spell_holy_water:
		case k_spell_holy_word:
		case k_spell_ice_bolt:
		case k_spell_implosion:
		case k_spell_lightning:
		case k_spell_magic_arrow:
		case k_spell_magic_fist:
		case k_spell_plague:		// This spell should be dealt with differently (effects everyone.)
		case k_spell_inferno:
		*/

		default:
			return ai_value_of_generic_damage_spell( hero, spell );
	}
}

float ai_value_of_bless_spell( t_hero const* hero, t_spell spell, t_creature_array const* array)
{
	// Generally the most powerful stack is 50% of the overall army value.
	// Our evaluation is assuming we're fighting an equal army.
	float army_value, best_value;
	t_creature_stack const* best_stack = array->ai_get_most_powerful(best_value, army_value);

	if (best_stack == NULL)
		return 0.0f;

	switch (spell) 
	{
		case k_spell_anti_magic:
		case k_spell_blur:
		case k_spell_cold_resistance:
		case k_spell_dispel:
		case k_spell_displacement:
		case k_spell_exorcism:
		case k_spell_fire_resistance:
		case k_spell_fear:
		case k_spell_first_strike:
		case k_spell_magic_resistance:
		case k_spell_martyr:
		case k_spell_pain_mirror:
		case k_spell_precision:  // Count the number of ranged creatures.
		case k_spell_protection_from_chaos:
		case k_spell_protection_from_death:
		case k_spell_protection_from_life:
		case k_spell_protection_from_nature:
		case k_spell_protection_from_order:
		case k_spell_quickness:
		case k_spell_reflexes:
		case k_spell_smoke:
		case k_spell_snake_strike:
		case k_spell_speed:
		case k_spell_terrain_walk:
		case k_spell_teleport:
			return ai_value_of_multiplicative_spell( best_value, spell );
	
		case k_spell_haste:
		case k_spell_nature_haste:
			return best_value * (ai_bonus_for_move_speed(2) + ai_bonus_for_speed(2) );

		case k_spell_fortune:
		case k_spell_luck_boost:
			return best_value * k_ai_luck_value;

		case k_spell_bless:
			return best_value * 0.09f;
		case k_spell_bloodlust:
			return best_value * 0.11f;
		case k_spell_bloodfrenzy:
			return army_value * 0.11f;
		case k_spell_celestial_armor:
			return ai_value_of_celestial_armor_spell( hero, array, army_value );
		case k_spell_divine_aura:
			return army_value * 0.11f;
		case k_spell_divine_protection:
			return best_value * 0.09f;
		case k_spell_freezing_attack:
			return best_value * 0.30f;
		case k_spell_health_boost:
			return ai_value_of_additional_hit_points( best_stack, best_stack->get_hit_points() * 0.25f );
		case k_spell_holy_armor:
			return best_value * 0.11f;
		case k_spell_giant_strength:
			return best_value * 0.20f;
		case k_spell_mass_bless:
			return army_value * 0.09f;
		case k_spell_mass_fortune:
			return army_value * k_ai_luck_value;
		case k_spell_mass_haste:
			return army_value * (ai_bonus_for_move_speed(2) + ai_bonus_for_speed(2) );
		case k_spell_mass_slayer:
			return army_value * ai_get_slayer_value_multiplier(array->get_map()->get_day()) * 0.25f;
		case k_spell_mass_spiritual_fervor:
			return k_ai_morale_value * army_value;
		case k_spell_morale_boost:
			return k_ai_morale_value * 0.5f * best_value;
		case k_spell_prayer:
			return ai_value_of_prayer_spell( hero, array );
		case k_spell_slayer:
			return best_value * ai_get_slayer_value_multiplier(array->get_map()->get_day()) * 0.25f;
		case k_spell_spiritual_fervor:
			return k_ai_morale_value * best_value;
		case k_spell_stoneskin:
		case k_spell_strength:
			return best_value * 0.11f;
		case k_spell_vampiric_touch:
			return ai_value_of_vampiric_touch( hero, array );

		// The following isn't being used by the AI
		case k_spell_quicksand:
			return army_value * 0.005;

		// The following are intended to fall thru to the default.
		/*
		case k_spell_guardian_angel:
		case k_spell_magic_mirror: 
		case k_spell_mass_blur:
		case k_spell_mana_flare:
		case k_spell_mass_exorcism:
		case k_spell_mass_first_strike:
		case k_spell_mass_precision:
		case k_spell_mass_protection_from_chaos:
		case k_spell_mass_protection_from_death:
		case k_spell_mass_protection_from_nature:
		case k_spell_mass_protection_from_order:
		case k_spell_mass_snake_strike:
		case k_spell_sacred_ground:
		*/

		default:
			return ai_value_of_multiplicative_spell( army_value, spell );			
	}
}

float ai_value_of_spell( t_hero const* hero, t_spell spell, t_creature_array const* array, bool ignore_mana_and_skills )
{
	if ( !ignore_mana_and_skills && 
			(!hero->can_learn( spell ) 
			|| hero->get_maximum_spell_points() < get_spell_cost( spell )) ) 
		return 0.0f;

	switch ( get_ai_spell_type( spell ) )
	{
		case k_ai_spell_type_blessing:
			return ai_value_of_bless_spell( hero, spell, array );
		case k_ai_spell_type_curse:
			return ai_value_of_curse_spell( hero, spell, array );
		case k_ai_spell_type_damage:
			return ai_value_of_damage_spell( hero, spell, array );
		case k_ai_spell_type_healing:
			return ai_value_of_healing_spell( hero, spell, array );
		case k_ai_spell_type_summoning:
			return ai_value_of_summoning_spell( hero, spell );
		default:
			return ai_value_of_generic_spell( hero, spell, array);
	}	
}

t_spell ai_get_best_spell( t_hero const* hero, t_creature_array const* array, int& spell_damage )
{
	float best_damage = 0.0f;
	t_spell best_spell = k_spell_none;
	
	t_adventure_ai::t_damage_spells const& spells = array->get_map()->get_ai().get_damage_spells();
	int size = spells.size();
	for (int i = 0; i < size; i++)
	{
		t_spell spell = spells[i];
		if (!hero->in_spellbook(spell)) continue;

		float damage = ai_get_spell_damage( spell, hero );
		if (damage > best_damage) 
		{
			best_spell = spell;
			best_damage = damage;
		}
	}

	spell_damage = best_damage;
	
	return best_spell;
}

// ********************************************
// Artifact Value Query Functions/Classes
// ********************************************

const float k_ai_artifact_hoarding_value = 10.0f;

// Nearby creatures take no dmg bonus when fighting hand to hand.
const float k_ai_value_of_normal_melee = 0.05f; 

t_artifact_value_query::t_artifact_value_query(	t_creature_array const* _array, 
														t_creature_stack const* _stack,
														t_artifact_type _artifact )
	: array(_array), stack(_stack),	new_value(0.0f), 
		existing_value(0.0f), hoard(false)
{
	artifact = get_default_artifact(_artifact);
}

t_artifact_value_query::t_artifact_value_query()
	: array(NULL), stack(NULL),	new_value(0.0f), 
		existing_value(0.0f), hoard(false)
{
}

float get_generic_hero_artifact_effect_value( t_artifact const& artifact )
{
	float value = 0.10f;
	if (get_level(artifact.get_icon()) == k_artifact_level_major)
		value *= 2;
	return value;
}

float get_generic_creature_artifact_effect_value( t_artifact const& artifact )
{
	float value = 0.05f;
	if (get_level(artifact.get_icon()) == k_artifact_level_major)
		value *= 2;
	return value;
}

class t_artifact_value_visitor : public t_artifact_effect_visitor
{
public:
	t_artifact_value_visitor( t_creature_stack const* stack, t_creature_array const* array );

	float			get_value() const;

	void			clear_value();
	
	virtual bool	visit_ability( t_artifact_prop::t_give_ability& effect );
	virtual bool	visit_aligned_bonus( t_artifact_prop::t_aligned_bonus_base& effect );
	virtual bool	visit_combat( t_artifact_prop::t_combat& effect );
	virtual bool	visit_damage( t_artifact_prop::t_damage_bonus_base& effect );
	virtual bool	visit_growth( t_artifact_prop::t_creature_growth& effect );
	virtual bool	visit_income( t_artifact_prop::t_income& effect );
	virtual bool	visit_movement( t_artifact_prop::t_movement& effect );
	virtual bool	visit_skill( t_artifact_prop::t_skill_effect& effect );
	virtual bool	visit_spell( t_artifact_prop::t_single_spell& effect );
	virtual bool	visit_spell_attack( t_artifact_prop::t_spell_with_attack_base& effect );
	virtual bool	visit_spell_charges( t_artifact_prop::t_spell_charges_base& effect );
	virtual bool	visit_spell_cost( t_artifact_prop::t_spell_cost_base& effect );
	virtual bool	visit_spell_list( t_artifact_prop::t_spell_list_effect& effect );
					
protected:
	t_creature_array const*	m_array;
	t_creature_stack const*	m_stack;
	t_player const*			m_owner;
	float					m_value;
};

t_artifact_value_visitor::t_artifact_value_visitor( t_creature_stack const* stack, 
														t_creature_array const* array )
	: m_value( 0.0f ), m_stack( stack ), m_array( array ), m_owner(NULL)
{
	assert(m_array && m_stack);
	m_owner = m_array->get_owner();
	assert(m_owner);
}

inline float t_artifact_value_visitor::get_value() const
{
	return m_value;
}

inline void t_artifact_value_visitor::clear_value()
{
	m_value = 0.0f;
}

bool t_artifact_value_visitor::visit_ability( t_artifact_prop::t_give_ability& effect )
{
	t_hero const* hero = m_stack->get_const_hero();
	if (hero == NULL)
		return false;

	switch (effect.get_ability())
	{
		case k_ability_ignore_wards:
		case k_ability_ignore_zones_of_control:
		case k_ability_first_strike_immunity:
		case k_ability_negate_mind_immunity:
		case k_ability_long_range:
		case k_ability_long_weapon:
		case k_ability_panic:
		case k_ability_stunning:
		case k_ability_ranged_stun:
		case k_ability_regeneration:
		case k_ability_vampire:
			m_value += m_stack->ai_value() * get_generic_hero_artifact_effect_value( *m_artifact ) ;
			break;

		case k_ability_area_effect:
			m_value += ai_value_of_additional_damage( hero, hero->ai_get_total_offense( k_offense_ranged ) * 0.30, k_offense_ranged );
			break;
		
		case k_ability_extra_melee_attack:
			// Cut the value of this in half if the hero doesn't 
			// prefer his melee attack.
			m_value += m_stack->ai_value() 
					* k_ai_value_of_extra_strike 
					* ai_is_hero_ranged(hero) ? 0.5f : 1.00f;
			break;
		case k_ability_extra_ranged_attack:
			// Cut the value of this in half if the hero doesn't 
			// prefer his ranged attack.
			m_value += m_stack->ai_value() 
					* k_ai_value_of_extra_strike 
					* ai_is_hero_ranged(hero) ? 1.0f : 0.50f;
			break;
		case k_ability_melee_giantslayer:
			m_value += m_stack->ai_value() * (0.25f / 4)
						* ai_is_hero_ranged(hero) ? 0.50f : 1.0f;
			break;
		case k_ability_normal_melee:
			m_value += m_array->ai_value_creatures() * k_ai_value_of_normal_melee;
			break;
		case k_ability_ranged_giantslayer:
			m_value += m_stack->ai_value() * (0.25f / 4)
						* ai_is_hero_ranged(hero) ? 1.0f : 0.50f;
			break;
		case k_ability_spell_vulnerability:
			// I never want to put this on.  Using it correctly is too complicated.
			break;			
	}
	return false;
}

bool t_artifact_value_visitor::visit_aligned_bonus( t_artifact_prop::t_aligned_bonus_base& effect )
{
	// The AI isn't going to try to use these objects.
	return false;
}

bool t_artifact_value_visitor::visit_combat( t_artifact_prop::t_combat& effect )
{
	t_hero const* hero = m_stack->get_const_hero();
	if (!hero)
		return false;

	t_artifact_target target = effect.get_target();
	switch (effect.get_effect()) 
	{
		case k_artifact_effect_defense:
			{
				float value_multiplier = (effect.get_amount() / 200);
				if (target == k_artifact_target_hero) 
					m_value += hero->ai_value() * value_multiplier;
				else
					m_value += m_array->ai_value_creatures() * value_multiplier;
			}
			break;
		case k_artifact_effect_demonslayer:
			{
				float hero_value = 0.3f * hero->ai_value();
				float stack_value = m_array->ai_value_creatures() * 0.2247f; 
				m_value += hero_value + stack_value;
			}
			break;
		case k_artifact_effect_weapon:
			{
				bool ranged = false;
				if ((ranged = effect.affects_ranged()) && effect.affects_melee())
					ranged = ai_is_hero_ranged( hero );
				m_value += ai_value_of_additional_damage( hero, effect.get_amount(), ranged ? k_offense_ranged : k_offense_melee );
			}
			break;
	}
	return false;
}

bool t_artifact_value_visitor::visit_damage( t_artifact_prop::t_damage_bonus_base& effect )
{
	t_hero const* hero = m_stack->get_const_hero();
	if (hero == NULL)
		return false;

	int bonus_multiplier = hero->get_level() / effect.get_levels_per_bonus();
	int damage = effect.get_amount() + effect.get_additional_bonus() * bonus_multiplier;
	
	bool ranged = false;
	if ((ranged = effect.affects_ranged()) && effect.affects_melee())
		ranged = ai_is_hero_ranged( hero );

	m_value += ai_value_of_additional_damage(hero, damage);
	
	return false;
}

bool t_artifact_value_visitor::visit_growth( t_artifact_prop::t_creature_growth& effect )
{
	for (int i = 0; i < k_creature_level_count; i++)
	{
		int bonus = effect.get_bonus(i);
		if (bonus)
			m_value += m_owner->get_weekly_creature_growth( i ) * (bonus / 100);
	}
	return false;
}

bool t_artifact_value_visitor::visit_income( t_artifact_prop::t_income& effect )
{
	t_material_array const& array = effect.get_bonus();
	t_material_array materials = m_owner->get_valued_funds( m_owner->get_funds() );
	m_value += materials.get_sum();
	return false;
}

bool t_artifact_value_visitor::visit_movement( t_artifact_prop::t_movement& effect )
{	
	bool sea = false;
	float amount = effect.get_amount() / 200.0f;  // (50% of the increase is the bonus)
  sea = effect.affects_sea();
	if (! (effect.affects_land() && sea) )
	{
		float multiplier = m_array->get_map()->get_ai().get_land_coverage();
		if (sea)
			multiplier = 1.0f - multiplier;
		amount *= multiplier;
	} 
	m_value += amount;
		
	return false;
}

bool t_artifact_value_visitor::visit_skill( t_artifact_prop::t_skill_effect& effect )
{
	t_hero const* hero = m_stack->get_const_hero();
	if (!hero)
		return false;

	t_skill_mastery mastery;
	t_artifact_target target = effect.get_target();
	switch (effect.get_effect()) 
	{
		case k_artifact_effect_skill:
			switch (effect.get_skill())
			{
				case k_skill_necromancy:
					mastery = hero->get_skill(k_skill_death_magic);
					if (mastery != k_mastery_none) 
						m_value += k_necro_factor[ hero->get_skill(k_skill_death_magic) + 1] * (effect.get_amount() / 100.0f) * m_array->ai_value();
					break;
				case k_skill_resurrection:
					mastery = hero->get_skill(k_skill_life_magic);
					if (mastery != k_mastery_none) 
						m_value += k_res_factor[ hero->get_skill(k_skill_life_magic) + 1] * (effect.get_amount() / 100.0f) * m_array->ai_value();
					break;
				case k_skill_charm:
					mastery = hero->get_skill(k_skill_nature_magic);
					if (mastery != k_mastery_none) 
						m_value += k_res_factor[ hero->get_skill(k_skill_nature_magic) + 1] * (effect.get_amount() / 100.0f) * m_array->ai_value();
					break;
				case k_skill_diplomacy:
					break;
			}
			break;
		case k_artifact_effect_skill_bonus:
			{
				mastery = hero->get_skill(effect.get_skill());
				if (mastery == k_mastery_grand_master)
					return false;
				t_skill skill(effect.get_skill(), enum_add(mastery, 1));
				m_value += ai_value_of_skill_increase( hero, skill, m_array);
			}
			break;
	}
	return false;
}

bool t_artifact_value_visitor::visit_spell( t_artifact_prop::t_single_spell& effect )
{
	t_hero const* hero = m_stack->get_const_hero();
	if (hero == NULL)
		return false;

	float value = ai_value_of_spell( hero, effect.get_spell(), m_array, true );
	
	if (value > 0.0f && ( effect.get_effect() == k_artifact_effect_spell ))
		value *= 0.20f;

	m_value += value;

	return false;
}

bool t_artifact_value_visitor::visit_spell_attack( t_artifact_prop::t_spell_with_attack_base& effect )
{
	t_hero const* hero = m_stack->get_const_hero();
	if (!hero)
		return false;

	float damage = ai_get_spell_damage( effect.get_spell(), hero );

	bool ranged = effect.affects_ranged();
	if (effect.affects_melee() && ranged)
		ranged = ai_is_hero_ranged(hero);
	
	m_value += ai_value_of_additional_damage( hero, damage );
	
	return false;
}

bool t_artifact_value_visitor::visit_spell_charges( t_artifact_prop::t_spell_charges_base& effect )
{
	t_hero const* hero = m_stack->get_const_hero();
	if (!hero) 
		return false;

	m_value += ai_value_of_spell( hero, effect.get_spell(), m_array, true );

	return false;
}

bool t_artifact_value_visitor::visit_spell_cost( t_artifact_prop::t_spell_cost_base& effect )
{
	// Place value calculation here.
	return false;
}

bool t_artifact_value_visitor::visit_spell_list( t_artifact_prop::t_spell_list_effect& effect )
{
	t_hero const* hero = m_stack->get_const_hero();
	if (!hero) 
		return false;

	switch( effect.get_effect() )
	{
		case k_artifact_effect_immunity:
			{
				t_artifact_prop::t_spell_set spells = effect.get_spells();
				t_artifact_prop::t_spell_set::const_iterator iter = spells.begin();
				int total_levels = 0;
				while (iter != spells.end())
				{
					t_spell spell = *iter++;
					t_ai_spell_type type = get_ai_spell_type( spell );
					if ((type == k_ai_spell_type_blessing) 
						|| (type == k_ai_spell_type_summoning) 
						|| (type == k_ai_spell_type_healing) )
						continue;
					total_levels += get_spell_level( spell );
				}
				int spell_immunity_levels = m_array->get_map()->get_ai().get_spell_immunity_levels();
				
				float target_value;
				if (effect.get_target() == k_artifact_target_hero)
					target_value = hero->ai_value();
				else 
					target_value = m_array->ai_value();
				
				m_value += total_levels / spell_immunity_levels * 0.30f * target_value;
			}
			break;	
		case k_artifact_effect_spell_book:
			{
				t_artifact_prop::t_spell_set spells = effect.get_spells();
				t_artifact_prop::t_spell_set::const_iterator iter = spells.begin();
				float best_value = 0.0f;
				while (iter != spells.end())
				{
					t_spell spell = *iter++;
					float value = ai_value_of_spell(hero, spell, m_array);
					if (value > best_value)
						best_value = value;
				}
				m_value += best_value;
			}
			break;
		case k_artifact_effect_spell_bonus:
			{
				t_artifact_prop::t_spell_set spells = effect.get_spells();
				t_artifact_prop::t_spell_set::const_iterator iter = spells.begin();
				float best_value = 0.0f;
				t_spell best_spell = k_spell_none;
				while (iter != spells.end())
				{
					t_spell spell = *iter++;
					float value = ai_value_of_spell(hero, spell, m_array);
					if (value > best_value) 
					{
						best_spell = spell;
						best_value = value;
					}
				}
				
				// This is rather nieve, especially for damage spells...  it should
				// be adequate, though sometimes inaccurate.
				if (best_spell != k_spell_none)
					best_value *= effect.get_amount() / 100.0f;
			}
			break;
	}
	return false;
}

// Artifact value query fuctions

float ai_value_of_artifact( t_artifact_value_query& params )
{
	t_hero const* hero = params.stack->get_const_hero();

	if (hero == NULL)
		return 0.0f;

	t_artifact_value_visitor value_visitor( params.stack, params.array );
	params.artifact.accept( value_visitor );
	float new_value = value_visitor.get_value();
	value_visitor.clear_value();
	float existing_value = 0.0f;

	t_artifact_slot slot = params.artifact.get_slot();

	switch (slot) 
	{
		case k_artifact_slot_2_handed:
			{
				t_artifact const& weapon = params.stack->get_artifact( k_artifact_slot_weapon );
				t_artifact const& shield = params.stack->get_artifact( k_artifact_slot_shield );

				if ( weapon.get_icon() != k_artifact_none )
				{
					weapon.accept( value_visitor );
					params.displacement.push_back( k_artifact_slot_weapon );
				}
				if ( shield.get_icon() != k_artifact_none )
				{
					shield.accept( value_visitor );
					params.displacement.push_back( k_artifact_slot_shield );
				}
				existing_value = value_visitor.get_value();
			} 
			break;
		case k_artifact_slot_misc:
			{
				bool empty_slot = false;
				for (int i = 0; i < 4; i++) {
					if ( params.stack->get_artifact( enum_add( k_artifact_slot_misc_1, i ) ).get_icon() == k_artifact_none)
					{
						empty_slot = true;
						break;
					}
				}
				if (!empty_slot) {
					float lowest_value = 0.0f;
					t_artifact_slot displaced_slot = k_artifact_slot_none;
					for (t_artifact_slot slot(k_artifact_slot_misc_1); slot <= k_artifact_slot_misc_4; enum_incr(slot)) 
					{
						t_artifact const& misc_artifact = params.stack->get_artifact( slot );
						misc_artifact.accept( value_visitor );
						existing_value = value_visitor.get_value();
						
						if ( (slot == k_artifact_slot_misc_1) || ( lowest_value > existing_value ) )
						{
							lowest_value = existing_value;
							displaced_slot = slot;
						}
						
						value_visitor.clear_value();
					}
					existing_value = lowest_value;
					params.displacement.push_back( displaced_slot );
				}
			}
			break;
		case k_artifact_slot_ring:
			{
				t_artifact const& right_ring = params.stack->get_artifact( k_artifact_slot_right_ring );
				t_artifact const& left_ring = params.stack->get_artifact( k_artifact_slot_left_ring );

				if ( (right_ring.get_icon() == k_artifact_none) || (left_ring.get_icon() == k_artifact_none) )
					break;

				right_ring.accept( value_visitor );
				float right_value = value_visitor.get_value();
				value_visitor.clear_value();

				left_ring.accept( value_visitor );
				if ( right_value > value_visitor.get_value() ) 
				{
					params.displacement.push_back( k_artifact_slot_left_ring );
					existing_value = value_visitor.get_value();
				}
				else
				{
					params.displacement.push_back( k_artifact_slot_right_ring );
					existing_value = right_value;
				}
			}
			break;
		case k_artifact_slot_potion:
			{
				// Don't hoard more than one of any given potion type.
				int count = hero->get_backpack_count();
				for (int i = 0; i < count; i++)
				{
					if (hero->get_backpack_slot( i ).get_icon() == params.artifact.get_icon())
					{
						new_value = 0.0f;
						break;
					}
				}
			}
			break;
	}

	params.existing_value = existing_value;
	if (existing_value >= new_value)
	{
		params.hoard = true;
		params.displacement.clear();
	}
	params.new_value = new_value;

	return params.get_value();
}

float ai_value_of_artifact_simple( t_creature_array const& army, t_artifact_type artifact )
{
	float best_value = 0.0f;
	for (int i = 0; i < t_creature_array::k_size; i++)
	{
		t_hero const* hero = army[i].get_const_hero();
		if (!hero) continue;
	
		t_artifact_value_query value( &army, hero, artifact );
		
		float new_value = ai_value_of_artifact( value );

		if (new_value > best_value)
			best_value = new_value;
	}

	return best_value;
}	

float ai_value_of_abstract_artifact( t_creature_array const& army, t_artifact_type artifact )
{
	t_artifact_level level = get_level(artifact);

	if ((level >= k_artifact_level_item) && (level <= k_artifact_level_major))
		return army.get_owner()->ai_get_artifact_value(level);
	else 
		// This is really a safety-net more than anything.  If we don't have 
		// abstract information calculated for the artifact level I use the 
		// real value.
		return ai_value_of_artifact_simple( army, artifact );
}

float ai_give_artifact_to_army( t_creature_array& array, t_artifact const& artifact )
{
	float current_value = 0.0f;
	const int depth_limit = 4;
	int depth_count = 0;

	std::vector< t_artifact > distribute;
	distribute.push_back( artifact );

	while (!distribute.empty() && (depth_limit > depth_count)) 
	{
		depth_count++;
		t_artifact current_artifact = distribute.back();
		distribute.pop_back();
		float best_value = 0.0f;
		t_creature_stack* best_stack = NULL;
		t_artifact_value_query best_query;
		for (int i = 0; i < t_creature_array::k_size; i++)
		{
			t_creature_stack& stack = array[i];
			if (stack.get_number() == 0)
				continue;
			
			t_artifact_value_query query_value( &array, &stack, current_artifact.get_icon() );
			float value = ai_value_of_artifact( query_value );

			if ( (best_value < value) || (best_stack == NULL) )
			{
				best_stack = &stack;
				best_query = query_value;
				best_value = value;
			}
		}

		current_value += best_query.get_value();
		int count = best_query.displacement.size();
		for ( int i = 0; i < count; i++ )
		{
			distribute.push_back( best_stack->get_artifact( best_query.displacement[i] ) );
			best_stack->remove( best_query.displacement[i] );
		}

		t_hero* hero = best_stack->get_hero();
		if (hero 
			&& (current_artifact.get_slot() != k_artifact_slot_none) 
			&& (current_artifact.get_slot() != k_artifact_slot_potion)
			&& (!best_query.hoard))
			
		{
			if ( !hero->equip( current_artifact ) )
			{
				best_stack->add_to_backpack( artifact );
			}
		} else 
			best_stack->add_to_backpack( artifact );
	}

	t_creature_stack* target_stack = NULL;
	while (!distribute.empty()) 
	{
		if (target_stack == NULL)
		{
			float best_value = 0.0f;
			for (int i = 0; i < t_creature_array::k_size; i++)
			{
				float value = array[i].ai_value();
				if (value > best_value)
				{
					best_value = value;
					target_stack = &array[i];
				}
			}
			if (target_stack == NULL) 
				return current_value;
		}
		
		target_stack->add_to_backpack( distribute.back() );
		distribute.pop_back();
	}
	return current_value;
}

float ai_value_of_artifact_complex( t_creature_array const& array, t_artifact const& artifact )
{
	std::auto_ptr<t_creature_array> temp_army_ptr = ai_deep_copy_of_creature_array( array );

	return ai_give_artifact_to_army( *temp_army_ptr, artifact);
}
